package com.dht.xiao.jianye.base.serialport.asyncservices;

import android.os.HandlerThread;
import android.text.TextUtils;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.dht.xiao.jianye.base.exception.ApiException;
import com.dht.xiao.jianye.base.exception.ApiExceptionCode;
import com.dht.xiao.jianye.base.serialport.CmdPack;
import com.dht.xiao.jianye.base.serialport.DataPack;
import com.dht.xiao.jianye.base.serialport.asyncservices.message.ReadMessage;
import com.dht.xiao.jianye.base.serialport.asyncservices.thread.SerialReadThread;
import com.dht.xiao.jianye.base.serialport.bean.CmdBean;
import com.dht.xiao.jianye.base.serialport.callback.BaseDataCallback;
import com.dht.xiao.jianye.base.serialport.callback.SendResultCallback;
import com.dht.xiao.jianye.base.serialport.log.SerialPortLoggerFactory;
import com.dht.xiao.jianye.base.serialport.utils.ByteUtil;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

/**
 * @author :      fangbingran
 * @aescription : todo(这里用一句话描述这个类的作用)
 * @date :        2019/06/06  10:13
 */
public class AsyncServicesProxy {

    private boolean isStart = false;
    private BaseDataCallback mBaseDataCallback;
    private OutputStream mOutputStream;
    private BufferedInputStream mBufferedInputStream;
    private SerialReadThread mReadThread;
    private HandlerThread mWriteThread;
    private Scheduler mSendScheduler;
    private boolean isActivelyReceivedCommand;
    private List<byte[]> mHeartCommand;
    private List<CmdBean> mList = new ArrayList<>();

    public AsyncServicesProxy(boolean isActivelyReceivedCommand, List<byte[]> heartCommand, OutputStream outputStream,
                              BufferedInputStream bufferedInputStream, BaseDataCallback baseDataCallback) {
        mBaseDataCallback = baseDataCallback;
        mOutputStream = outputStream;
        mBufferedInputStream = bufferedInputStream;
        mHeartCommand = heartCommand;
        this.isActivelyReceivedCommand = isActivelyReceivedCommand;
        startTask();
        EventBus.getDefault().register(this);
    }

    public void startTask() {
        if (mReadThread == null) {
            mReadThread = new SerialReadThread(isActivelyReceivedCommand, mHeartCommand, mBufferedInputStream,
                    mBaseDataCallback);
        }
        if (mWriteThread == null) {
            mWriteThread = new HandlerThread("async-services-thread");
        }
        mWriteThread.start();
        if (mSendScheduler == null) {
            mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
        }
        if (!isStart) {
            mReadThread.start();
            isStart = true;
        }
    }

    /**
     * 停止当前任务,释放线程
     */
    public void stopTask() {
        if (isStart) {
            if (mReadThread != null) {
                mReadThread.close();
            }
            if (mWriteThread != null) {
                mWriteThread.quit();
            }
            isStart = false;
        }
    }

    private Disposable mDisposable;

    public void send(CmdPack cmdPack, SendResultCallback sendResultCallback) {
        synchronized (mList) {
            long time = System.currentTimeMillis();
            if (mCmdBean != null) {
                mList.add(new CmdBean(cmdPack, sendResultCallback, time + "", 0));
                return;
            }
            mCmdBean = new CmdBean(cmdPack, sendResultCallback, time + "", 0);
            mList.add(mCmdBean);
            dispose();
            rxSendData(mCmdBean).subscribeOn(mSendScheduler)
                    .subscribe(new Observer<CmdBean>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                            mDisposable = d;
                            //SerialPortLoggerFactory.info("发送数据+Disposable:" + d.isDisposed());
                        }

                        @Override
                        public void onNext(CmdBean cmdBean) {
                            SerialPortLoggerFactory.info("发送数据:" + ByteUtil.bytes2HexStr(cmdBean.getCmdPack().getSendData()));
                        }

                        @Override
                        public void onError(Throwable e) {
                            //                Log.e("发送失败", e.toString());
                        }

                        @Override
                        public void onComplete() {

                        }
                    });
        }
    }

    private CmdBean mCmdBean;

    /**
     * (rx包裹)发送数据
     *
     * @return
     */
    private Observable<CmdBean> rxSendData(final CmdBean cmdBean) {
        return Observable.create(new ObservableOnSubscribe<CmdBean>() {
            @Override
            public void subscribe(ObservableEmitter<CmdBean> emitter) throws Exception {
                try {
                    mOutputStream.write(cmdBean.getCmdPack().getSendData());
                    long time = System.currentTimeMillis();
                    cmdBean.setTime(time);
                    emitter.onNext(cmdBean);
                } catch (Exception e) {
                    SerialPortLoggerFactory.error("发送："
                            + ByteUtil.bytes2HexStr(cmdBean.getCmdPack().getSendData())
                            + " 失败,"
                            + e.toString());
                    if (cmdBean.getSendResultCallback() != null) {
                        cmdBean.getSendResultCallback().onFailed(
                                new ApiException(ApiExceptionCode.SERIAL_PORT_ERROR,
                                        "硬件错误:" + e.toString()));
                    }
                    removeCmdBean();
                    if (!emitter.isDisposed()) {
                        emitter.onError(e);
                        return;
                    }
                }
                emitter.onComplete();
            }
        });
    }

    private void removeCmdBean() {
        if (mCmdBean == null) {
            return;
        }
        synchronized (mList) {
            Iterator<CmdBean> it = mList.iterator();
            while (it.hasNext()) {
                CmdBean cmdBean = it.next();
                if (cmdBean != null) {
                    if (TextUtils.equals(cmdBean.getKey(), mCmdBean.getKey())) {
                        it.remove();
                        mCmdBean = null;
                        return;
                    }
                }
            }
        }
    }

    /**
     * 下一个数据发送
     */
    private synchronized void nextSend() {
        if (mList.size() == 0) {
            return;
        }
        //防止并发问题
        if (mCmdBean != null) {
            return;
        }
        mCmdBean = mList.get(0);
        dispose();
        rxSendData(mCmdBean).subscribeOn(mSendScheduler)
                .subscribe(new Observer<CmdBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        mDisposable = d;
                      //  SerialPortLoggerFactory.info("发送数据+Disposable:" + d.isDisposed());
                    }

                    @Override
                    public void onNext(CmdBean cmdBean) {
                        SerialPortLoggerFactory.info("发送数据:" + ByteUtil.bytes2HexStr(cmdBean.getCmdPack().getSendData()));
                    }

                    @Override
                    public void onError(Throwable e) {
                        //                Log.e("发送失败", e.toString());
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    private void dispose() {
        if (mDisposable != null && !mDisposable.isDisposed()) {
            mDisposable.dispose();
            SerialPortLoggerFactory.info("发送数据+Disposable释放");
        }
        mDisposable = null;
    }

    /**
     * 关闭串口
     */
    public void close() {
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (mBufferedInputStream != null) {
            try {
                mBufferedInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (mReadThread != null) {
            mReadThread.close();
        }
        if (mWriteThread != null) {
            mWriteThread.quit();
        }
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessageEvent(ReadMessage message) {

        if (message != null) {
            switch (message.getReadType()) {
                case ReadType.CheckSendData:
                    checkSendData();
                    break;
                case ReadType.ReadDataSuccess:
                    if (message.getDataPack() != null) {
                        checkReadDataSuccess(message.getDataPack());
                    }
                    break;
                default:
            }
        }
    }

    /**
     * 校验成功数据
     */
    private void checkReadDataSuccess(DataPack dataPack) {
        dispose();
        if (mCmdBean == null) {
            nextSend();
            return;
        }
        synchronized (mList) {
            Iterator<CmdBean> it = mList.iterator();
            String readCommand = ByteUtil.bytes2HexStr(dataPack.getCommand());
            while (it.hasNext()) {
                CmdBean cmdBean = it.next();
                if (cmdBean != null) {
                    if (TextUtils.equals(cmdBean.getKey(), mCmdBean.getKey())) {
                        boolean isRemove = false;
                        boolean isOnSuccess = false;
                        List<byte[]> list = null;
                        if (cmdBean.getCmdPack() != null && cmdBean.getCmdPack().getCheckCommand() != null) {
                            list = cmdBean.getCmdPack().getCheckCommand();
                            for (int i = 0; i < list.size(); i++) {
                                String command = ByteUtil.bytes2HexStr(list.get(i));
                                if (TextUtils.equals(command, readCommand)) {
                                    isOnSuccess = true;
                                    if (i == list.size() - 1) {
                                        isRemove = true;
                                    }
                                    break;
                                }
                                if (i == list.size() - 1) {
                                    isRemove = true;
                                }
                            }
                        }
                        if (isOnSuccess) {
                            if (mCmdBean.getSendResultCallback() != null) {
                                mCmdBean.getSendResultCallback().onSuccess(dataPack);
                            }
                        } else {
                            if (mCmdBean.getSendResultCallback() != null) {
                                mCmdBean.getSendResultCallback()
                                        .onFailed(new ApiException(ApiExceptionCode.SERIAL_PORT_ERROR,
                                                "命令码不同,获取到结果为:" + dataPack.toString() + "--校验命令码:" + mCmdBean.getCmdPack().getCheckCommands()));
                            }
                        }
                        if (isRemove) {
                            it.remove();
                            mCmdBean = null;
                            nextSend();
                        }
                        return;
                    }

                }
            }
        }
    }

    /**
     * 校验发送数据
     */
    private void checkSendData() {
        dispose();
        if (mCmdBean == null) {
            nextSend();
            return;
        }
        synchronized (mList) {
            Iterator<CmdBean> it = mList.iterator();
            while (it.hasNext()) {
                CmdBean cmdBean = it.next();
                if (cmdBean != null) {
                    if (TextUtils.equals(cmdBean.getKey(), mCmdBean.getKey())) {
                        if (mCmdBean.getTime() == 0) {
                            return;
                        }
                        if (System.currentTimeMillis() - mCmdBean.getTime() >= mCmdBean.getCmdPack()
                                .getWaitOutTime()) {
                            if (mCmdBean.getSendResultCallback() != null) {
                                mCmdBean.getSendResultCallback()
                                        .onFailed(new ApiException(
                                                ApiExceptionCode.SERIAL_PORT_READ_OUT_TIME_ERROR, "读取超时"));
                            }
                            it.remove();
                            mCmdBean = null;
                            nextSend();
                            return;
                        }
                    }
                }
            }
        }
    }
}
